/**
 * Copyright (c) 2011 Congine Information Technology Corporation, All rights reserved.
 * The Software is owned by Congine and is protected by copyright laws and other national laws.
 * You agree that you have no right, title or interest in the Software.
 * This code cannot simply be copied and put under another distribution license.
 * Copyright remains Congine's, and as such any Copyright notices in the code are not to be removed.
 */
package cn.congine.wizarpos.mall.dao.hibernate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import cn.congine.wizarpos.mall.common.Page;
import cn.congine.wizarpos.mall.dao.GenericDao;
import cn.congine.wizarpos.mall.model.BaseEntity;

/**
 * This class serves as the Base class for all other DAOs - namely to hold
 * common CRUD methods that they might all use. You should only need to extend
 * this class when your require custom CRUD logic.
 * 
 * To register this class in your Spring context file, use the following XML.
 * 
 * 
 * @param <T>
 *            a type variable
 * @param <PK>
 *            the primary key for that type
 * @author <a href="mailto:lizhen@congine.cn">Rime Lee</a>
 */
public class GenericDaoHibernate<T extends BaseEntity> implements GenericDao<T> {
	/**
	 * Log variable for all child classes. Uses LogFactory.getLog(getClass())
	 * from Commons Logging
	 */
	protected final Log log = LogFactory.getLog(getClass());

	private Class<T> persistentClass;

	@Resource(name = "sessionFactory")
	private SessionFactory sessionFactory;

	/**
	 * Constructor that takes in a class to see which type of entity to persist.
	 * Use this constructor when subclassing.
	 * 
	 * @param persistentClass
	 *            the class type you'd like to persist
	 */
	public GenericDaoHibernate(final Class<T> persistentClass) {
		this.persistentClass = persistentClass;
	}

	/**
	 * Constructor that takes in a class and sessionFactory for easy creation of
	 * DAO.
	 * 
	 * @param persistentClass
	 *            the class type you'd like to persist
	 * @param sessionFactory
	 *            the pre-configured Hibernate SessionFactory
	 */
	public GenericDaoHibernate(final Class<T> persistentClass,
			SessionFactory sessionFactory) {
		this.persistentClass = persistentClass;
		this.sessionFactory = sessionFactory;
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		Criteria criteria = getSession().createCriteria(persistentClass);
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		return criteria.list();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<T> getAllDistinct() {
		Collection result = new LinkedHashSet(getAll());
		return new ArrayList(result);
	}

	@Override
	public T refresh(T object) {
		getSession().refresh(object);
		return object;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public T get(String id) {
		return (T) getSession().get(this.persistentClass, id);
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean exists(String id) {
		Object entity = getSession().get(this.persistentClass, id);
		return entity != null;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public T save(T object) {
		return (T) getSession().merge(object);
	}

	@Override
	public void update(T object) {
		getSession().update(object);
	}

	/**
	 * {@inheritDoc}
	 */
	public void remove(String id) {
		T obj = this.get(id);
		if (obj != null) {
			getSession().delete(obj);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void remove(T object) {
		getSession().delete(object);
	}

	@SuppressWarnings("unchecked")
	public Page<T> readAll4Page(String hql, String orderByClause,
			Map<String, Object> params, Page<T> page) {
		int totalCount = getTotalCount(hql, params);
		page.setTotalCount(totalCount);
		if (totalCount % page.getPageSize() == 0) {
			page.setTotalPage(totalCount / page.getPageSize());
		} else {
			page.setTotalPage(totalCount / page.getPageSize() + 1);
		}
		Query query = getSession().createQuery(
				hql + ((orderByClause != null) ? orderByClause : ""));
		if (params != null && !params.isEmpty()) {
			query.setProperties(params);
		}
		query.setFirstResult(page.getPageSize() * (page.getPageNo() - 1));
		query.setMaxResults(page.getPageSize());
		List<T> list = query.list();
		page.setList(list);
		return page;
	}

	private int getTotalCount(String hql, Map<String, Object> params) {
		int i = hql.indexOf("from");
		if (i > 0) {
			hql = hql.substring(i);
		}
		String _hql = "select count(*) " + hql;
		Query query = getSession().createQuery(_hql);
		if (params != null && !params.isEmpty()) {
			query.setProperties(params);
		}
		int totalCount = 0;
		try {
			totalCount = ((Long) query.uniqueResult()).intValue();
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		return totalCount;
	}

	@Override
	public void removeAll(List<T> list) {
		for (T t : list) {
			remove(t);
		}
	}
}